#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
OCR API 服务 - 融合版
调用 ocr_service.py 中的 OCR 功能
"""

import os
import json
import io
import tempfile
from typing import List, Dict, Any, Optional
from pathlib import Path

from fastapi import FastAPI, UploadFile, File, HTTPException, Form
from fastapi.responses import JSONResponse
from pydantic import BaseModel
import uvicorn

# 导入 OCR 服务模块
from service.ocr_service import OCRItem, OCRResult, your_ocr_model, compare_ocr_results

class OCRItemSchema(BaseModel):
    """单个OCR识别项的响应格式"""
    text: str
    coordinates: List[float]  # [x1, y1, x2, y2]

class StructuredDataSchema(BaseModel):
    """结构化数据（印章+手写体）"""
    stamp: List[OCRItemSchema]
    handwritten: List[OCRItemSchema]

class FullLayoutSchema(BaseModel):
    """全量版面信息"""
    pages: List[Dict[str, Any]]  # 包含每页的所有元素

class OCRResponse(BaseModel):
    """OCR识别结果响应格式"""
    code: int = 200
    message: str = "success"
    structured_data: StructuredDataSchema  # 用于结果输出
    full_layout: FullLayoutSchema  # 用于篡改检测

class CompareResponse(BaseModel):
    """差异对比结果响应格式"""
    code: int = 200
    message: str = "success"
    data: Dict[str, Any]  # 包含各类型差异及篡改判断

# ==================== 模型状态检查 ====================

# 检查模型是否可用
def is_model_available():
    """检查模型是否可用"""
    return your_ocr_model is not None

# ==================== FastAPI 应用 ====================

app = FastAPI(title="OCR识别与差异对比接口", version="1.0.0")

# 内存临时存储（实际生产环境建议用Redis/数据库）
temp_storage = {}

@app.get("/")
async def root():
    """根路径"""
    return {"message": "OCR API Service", "version": "1.0.0"}

@app.get("/ocr/health")
async def health_check():
    """服务健康检查"""
    return {
        "status": "healthy", 
        "service": "ocr-api",
        "model_loaded": is_model_available()
    }

@app.post("/ocr/recognize", response_model=OCRResponse)
async def recognize(file: UploadFile = File(...)):
    """
    单文件OCR识别接口
    上传图片/PDF，返回：
    - structured_data: 印章和手写体的结构化数据（带坐标）
    - full_layout: 全量版面信息（用于后续对比）
    """
    if not your_ocr_model:
        raise HTTPException(status_code=503, detail="OCR模型未加载，服务不可用")
    
    try:
        # 读取文件二进制数据
        file_bytes = await file.read()
        print(f"✓ 接收到文件: {file.filename}, 大小: {len(file_bytes)} bytes")
        
        # 调用OCR模型
        ocr_result: OCRResult = your_ocr_model(file_bytes)
        
        # 构建结构化数据响应
        structured_data = {
            "stamp": [{"text": item.text, "coordinates": item.coordinates} for item in ocr_result.stamp],
            "handwritten": [{"text": item.text, "coordinates": item.coordinates} for item in ocr_result.handwritten]
        }
        
        # 构建全量版面信息
        full_layout = {"pages": ocr_result.full_layout}
        
        # 临时存储（用于后续对比）
        temp_storage[file.filename] = ocr_result
        
        print(f"✓ 识别完成: 印章{len(ocr_result.stamp)} 手写{len(ocr_result.handwritten)} 印刷{len(ocr_result.printed)}")
        
        return {
            "structured_data": structured_data,
            "full_layout": full_layout
        }
        
    except Exception as e:
        print(f"✗ 识别失败: {e}")
        raise HTTPException(status_code=500, detail=f"识别失败：{str(e)}")

@app.post("/ocr/compare", response_model=CompareResponse)
async def compare(
    old_file: UploadFile = File(...),
    new_file: UploadFile = File(...)
):
    """
    两文件差异对比接口
    上传两个文件进行OCR识别并对比差异
    包含印刷体差异用于判断是否被篡改
    """
    if not your_ocr_model:
        raise HTTPException(status_code=503, detail="OCR模型未加载，服务不可用")
    
    try:
        # 读取文件二进制数据
        old_file_bytes = await old_file.read()
        print(f"✓ 接收到文件: {old_file.filename}, 大小: {len(old_file_bytes)} bytes")
        
        # 读取文件二进制数据
        new_file_bytes = await new_file.read()
        print(f"✓ 接收到文件: {new_file.filename}, 大小: {len(new_file_bytes)} bytes")
        
        # 获取前后两次OCR结果
        old_result: OCRResult = your_ocr_model(old_file_bytes)
        new_result: OCRResult = your_ocr_model(new_file_bytes)
        
        # 对比差异（包含篡改判断）
        diff_result = compare_ocr_results(old_result, new_result)
        
        print(f"✓ 对比完成: {old_file.filename} vs {new_file.filename}")
        print(f"  篡改检测: {'是' if diff_result['is_tampered'] else '否'}")
        
        return {
            "code": 200,
            "message": "success",
            "data": diff_result
        }
        
    except HTTPException:
        raise
    except Exception as e:
        print(f"✗ 对比失败: {e}")
        import traceback
        traceback.print_exc()
        raise HTTPException(status_code=500, detail=f"对比失败：{str(e)}")

@app.get("/ocr/storage")
async def get_storage():
    """获取当前存储的文件列表"""
    return {
        "files": list(temp_storage.keys()),
        "count": len(temp_storage)
    }

@app.delete("/ocr/storage/{filename}")
async def clear_file(filename: str):
    """清除指定文件的存储"""
    if filename in temp_storage:
        del temp_storage[filename]
        return {"message": f"文件 {filename} 已清除"}
    else:
        raise HTTPException(status_code=404, detail="文件不存在")

@app.delete("/ocr/storage")
async def clear_all():
    """清除所有存储"""
    temp_storage.clear()
    return {"message": "所有文件已清除"}

# ==================== Lanxum 格式转换函数 ====================

def coordinates_to_rowinfo(coords: List[float]) -> str:
    """将坐标列表 [x1, y1, x2, y2] 转换为 rowinfo 格式 "x1-y1-x2-y2" (参考返回值4.txt格式)"""
    if len(coords) >= 4:
        # 格式化为小数格式，如 "993.0-1270.0-1330.0-1302.0"
        return f"{coords[0]:.1f}-{coords[1]:.1f}-{coords[2]:.1f}-{coords[3]:.1f}"
    return ""

def convert_seal_to_lanxum_format(items: List[OCRItem], page_index: int = 0) -> List[Dict[str, str]]:
    """将印章 OCRItem 列表转换为 lanxum 格式的 sealinfo"""
    result = []
    for item in items:
        # 将整个文本作为 topinfo，其他字段为空
        seal_item = {
            "topinfo": item.text,
            "centralinfo": "",
            "bottominfo": "",
            "typeinfo": ""
        }
        result.append(seal_item)
    return result

def convert_handwriting_to_lanxum_format(items: List[OCRItem], page_index: int = 0) -> List[Dict[str, str]]:
    """将手写体 OCRItem 列表转换为 lanxum 格式的 handwritinginfo"""
    result = []
    for item in items:
        # 从 full_layout 中获取页码，如果没有则使用 page_index
        pagedesc = str(page_index)
        if hasattr(item, 'page_index') and item.page_index is not None:
            pagedesc = str(item.page_index)
        
        handwriting_item = {
            "pagedesc": pagedesc,
            "rowdesc": "",
            "rowinfo": coordinates_to_rowinfo(item.coordinates),
            "rowvalue": item.text
        }
        result.append(handwriting_item)
    return result

def convert_print_to_lanxum_format(
    old_items: List[OCRItem], 
    new_items: List[OCRItem], 
    old_page_index: int = 0,
    new_page_index: int = 0
) -> List[Dict[str, str]]:
    """将印刷体 OCRItem 列表转换为 lanxum 格式的 printinfo，包含差异信息"""
    result = []
    
    # 创建坐标到文本的映射，用于对比（使用坐标的字符串表示作为key，因为浮点数比较可能不精确）
    def coords_to_key(coords: List[float]) -> str:
        """将坐标转换为字符串key，用于比较"""
        if len(coords) >= 4:
            return f"{coords[0]:.1f}-{coords[1]:.1f}-{coords[2]:.1f}-{coords[3]:.1f}"
        return ""
    
    old_coords_map = {coords_to_key(item.coordinates): item for item in old_items}
    new_coords_map = {coords_to_key(item.coordinates): item for item in new_items}
    
    # 找出所有唯一的坐标key
    all_coords_keys = set(old_coords_map.keys()) | set(new_coords_map.keys())
    
    for coords_key in all_coords_keys:
        old_item = old_coords_map.get(coords_key)
        new_item = new_coords_map.get(coords_key)
        
        if old_item and new_item:
            # 文本相同或不同都算 replace
            if old_item.text != new_item.text:
                updatetype = "replace"
            else:
                # 文本相同，也标记为 replace（参考返回值4.txt，相同文本也可能标记为replace）
                updatetype = "replace"
            
            pagedesc = str(new_page_index)
            if hasattr(new_item, 'page_index') and new_item.page_index is not None:
                pagedesc = str(new_item.page_index)
            
            result.append({
                "pagedesc": pagedesc,
                "rowdesc": "",
                "rowinfo": coordinates_to_rowinfo(new_item.coordinates),
                "rowvalue": new_item.text,
                "updatetype": updatetype
            })
        elif old_item and not new_item:
            # 删除：旧文件有，新文件没有
            pagedesc = str(old_page_index)
            if hasattr(old_item, 'page_index') and old_item.page_index is not None:
                pagedesc = str(old_item.page_index)
            
            result.append({
                "pagedesc": pagedesc,
                "rowdesc": "",
                "rowinfo": coordinates_to_rowinfo(old_item.coordinates),
                "rowvalue": old_item.text,
                "updatetype": "delete"
            })
        elif not old_item and new_item:
            # 插入：新文件有，旧文件没有
            pagedesc = str(new_page_index)
            if hasattr(new_item, 'page_index') and new_item.page_index is not None:
                pagedesc = str(new_item.page_index)
            
            result.append({
                "pagedesc": pagedesc,
                "rowdesc": "",
                "rowinfo": coordinates_to_rowinfo(new_item.coordinates),
                "rowvalue": new_item.text,
                "updatetype": "insert"
            })
    
    return result

def convert_ocr_result_to_lanxum_format(
    ocr_result: OCRResult,
    page_index: int = 0,
    is_source: bool = True
) -> Dict[str, Any]:
    """将 OCRResult 转换为 lanxum 格式的 contentinfo"""
    contentinfo = {
        "sealinfo": convert_seal_to_lanxum_format(ocr_result.stamp, page_index),
        "handwritinginfo": convert_handwriting_to_lanxum_format(ocr_result.handwritten, page_index),
        "checkboxinfo": [],  # 复选框信息，当前为空
        "printinfo": []  # 印刷信息，如果是 source 则先不填，在 compare 中填充
    }
    
    # 如果是 source，需要填充 printinfo（使用第一页）
    if is_source:
        if ocr_result.printed:
            # 从 full_layout 中获取页码信息
            page_num = 0
            if ocr_result.full_layout and "pages" in ocr_result.full_layout:
                if len(ocr_result.full_layout["pages"]) > 0:
                    page_info = ocr_result.full_layout["pages"][0]
                    if "page" in page_info:
                        page_num = page_info["page"] - 1  # 转换为0-based
            
            for item in ocr_result.printed:
                pagedesc = str(page_num)
                if hasattr(item, 'page_index') and item.page_index is not None:
                    pagedesc = str(item.page_index)
                
                contentinfo["printinfo"].append({
                    "pagedesc": pagedesc,
                    "rowdesc": "",
                    "rowinfo": coordinates_to_rowinfo(item.coordinates),
                    "rowvalue": item.text,
                    "updatetype": "replace"  # source 默认都是 replace
                })
    
    return contentinfo


@app.post("/lanxum/compare")
async def lanxum_compare(
    sourceFile: UploadFile = File(...),
    targetFile: UploadFile = File(...),
    bustype: str = Form("request"),
    fileno: str = Form(""),
    busno: str = Form("")
):
    """
    Lanxum 格式的差异对比接口
    参考返回值4.txt 的格式，返回 sourcecontentinfo 和 targetcontentinfo
    排除手写体和印章信息后，比较印刷体文本内容是否一致
    comparisonresult: 0-相同, 1-不同
    """
    if not your_ocr_model:
        raise HTTPException(status_code=503, detail="OCR模型未加载，服务不可用")
    
    try:
        # 读取文件二进制数据
        sourceFile_bytes = await sourceFile.read()
        print(f"✓ 接收到文件: {sourceFile.filename}, 大小: {len(sourceFile_bytes)} bytes")
        
        # 读取文件二进制数据
        targetFile_bytes = await targetFile.read()
        print(f"✓ 接收到文件: {targetFile.filename}, 大小: {len(targetFile_bytes)} bytes")
        
        # 获取前后两次OCR结果
        old_result: OCRResult = your_ocr_model(sourceFile_bytes)
        new_result: OCRResult = your_ocr_model(targetFile_bytes)
        
        # 获取页码信息（从 full_layout 中提取）
        old_page_index = 0
        new_page_index = 0
        if old_result.full_layout and "pages" in old_result.full_layout:
            if len(old_result.full_layout["pages"]) > 0:
                old_page_info = old_result.full_layout["pages"][0]
                if "page" in old_page_info:
                    old_page_index = old_page_info["page"] - 1
        
        if new_result.full_layout and "pages" in new_result.full_layout:
            if len(new_result.full_layout["pages"]) > 0:
                new_page_info = new_result.full_layout["pages"][0]
                if "page" in new_page_info:
                    new_page_index = new_page_info["page"] - 1
        
        # 转换 sourcecontentinfo（旧文件）
        sourcecontentinfo = convert_ocr_result_to_lanxum_format(
            old_result, 
            old_page_index, 
            is_source=True
        )
        
        # 转换 targetcontentinfo（新文件）
        targetcontentinfo = convert_ocr_result_to_lanxum_format(
            new_result, 
            new_page_index, 
            is_source=False
        )
        
        # 生成 targetcontentinfo 的 printinfo 差异信息（对比 old 和 new 的印刷体）
        # 参考返回值4.txt，targetcontentinfo 的 printinfo 包含所有差异信息
        targetcontentinfo["printinfo"] = convert_print_to_lanxum_format(
            old_result.printed,
            new_result.printed,
            old_page_index,
            new_page_index
        )
        
        # sourcecontentinfo 的 printinfo 也需要包含所有印刷体信息（参考返回值4.txt）
        # 如果 sourcecontentinfo 的 printinfo 为空，需要填充
        if not sourcecontentinfo["printinfo"] and old_result.printed:
            page_num = old_page_index
            for item in old_result.printed:
                pagedesc = str(page_num)
                if hasattr(item, 'page_index') and item.page_index is not None:
                    pagedesc = str(item.page_index)
                
                sourcecontentinfo["printinfo"].append({
                    "pagedesc": pagedesc,
                    "rowdesc": "",
                    "rowinfo": coordinates_to_rowinfo(item.coordinates),
                    "rowvalue": item.text,
                    "updatetype": "replace"  # source 默认都是 replace
                })
        
        # 比较印刷体文本内容（排除手写体和印章信息）
        # 提取所有印刷体文本，排序后比较
        def get_printed_texts(result: OCRResult) -> str:
            """提取所有印刷体文本，按顺序拼接"""
            if not result.printed:
                return ""
            # 按坐标排序（从上到下，从左到右）
            sorted_items = sorted(result.printed, key=lambda x: (x.coordinates[1], x.coordinates[0]))
            texts = [item.text.strip() for item in sorted_items if item.text.strip()]
            return " ".join(texts).strip()
        
        old_printed_text = get_printed_texts(old_result)
        new_printed_text = get_printed_texts(new_result)
        
        # 判断印刷体文本内容是否一致
        # comparisonresult: 0-相同, 1-不同
        is_printed_same = (old_printed_text == new_printed_text)
        comparisonresult = "0" if is_printed_same else "1"
        
        # 构建响应（参考返回值4.txt 格式）
        response = {
            "code": "200",
            "data": {
                "sourcecontentinfo": sourcecontentinfo,
                "targetcontentinfo": targetcontentinfo,
                "bustype": bustype,
                "fileno": fileno if fileno else f"{sourceFile.filename}_{targetFile.filename}",
                "busno": busno if busno else ""
            },
            "comparisonresult": comparisonresult,
            "message": "success",
            "requestid": ""
        }
        
        print(f"✓ Lanxum对比完成: {sourceFile.filename} vs {targetFile.filename}, 结果: {comparisonresult}")
        
        return response
        
    except Exception as e:
        print(f"✗ Lanxum对比失败: {e}")
        import traceback
        traceback.print_exc()
        raise HTTPException(status_code=500, detail=f"对比失败：{str(e)}")

# ==================== 启动服务 ====================

if __name__ == "__main__":
    import argparse
    
    parser = argparse.ArgumentParser(description="OCR API服务（标准版本）")
    parser.add_argument("--host", type=str, default="0.0.0.0", help="服务监听地址")
    parser.add_argument("--port", type=int, default=8000, help="服务监听端口")
    
    args = parser.parse_args()
    
    print("=" * 60)
    print("启动OCR API服务（标准版本）")
    print("=" * 60)
    print(f"模型状态: {'已加载' if is_model_available() else '模拟模式'}")
    print(f"服务地址: http://{args.host}:{args.port}")
    print(f"API文档: http://{args.host}:{args.port}/docs")
    print("=" * 60)
    
    uvicorn.run(app, host=args.host, port=args.port)
